{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:36.692297Z",
     "start_time": "2025-01-20T12:11:30.138859Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.000779Z",
     "start_time": "2025-01-20T12:11:36.694305Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "from torch.utils.data import random_split\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 这里用 random_split 按照 11 : 1 的比例来划分数据集\n",
    "train_ds, val_ds = random_split(train_ds, [55000, 5000], torch.Generator().manual_seed(seed))"
   ],
   "outputs": [],
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.008571Z",
     "start_time": "2025-01-20T12:11:40.002300Z"
    }
   },
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "# print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2856], [0.3202])\n",
    ") # 对每个通道进行标准化"
   ],
   "outputs": [],
   "execution_count": 3
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.017531Z",
     "start_time": "2025-01-20T12:11:40.009571Z"
    }
   },
   "source": [
    "img, label = train_ds[0]\n",
    "img.shape, label"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(torch.Size([1, 28, 28]), 9)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.027376Z",
     "start_time": "2025-01-20T12:11:40.019537Z"
    }
   },
   "source": [
    "from torch.utils.data.dataloader import DataLoader\n",
    "\n",
    "batch_size = 32\n",
    "# 从数据集到dataloader\n",
    "train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)\n",
    "val_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=False, num_workers=4)\n",
    "test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ],
   "outputs": [],
   "execution_count": 5
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.034706Z",
     "start_time": "2025-01-20T12:11:40.029382Z"
    }
   },
   "source": [
    "128*9"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1152"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 6
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "两层卷积，一层池化"
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.065928Z",
     "start_time": "2025-01-20T12:11:40.036713Z"
    }
   },
   "source": [
    "class CNN(nn.Module):\n",
    "    def __init__(self, activation=\"relu\"):\n",
    "        super().__init__()\n",
    "        self.activation = F.relu if activation == \"relu\" else F.selu\n",
    "        #输入通道数，图片是灰度图，所以是1，图片是彩色图，就是3，输出通道数，就是卷积核的个数（32,1,28,28）\n",
    "        \n",
    "        self.pool = nn.MaxPool2d(2, 2) #池化不能够改变通道数，池化核大小为2（2*2），步长为2  (28-2)//2+1=14\n",
    "        # 输出图像尺寸：1+(n-k)//s, n为输入图像尺寸，k为卷积核尺寸，s为步长\n",
    "        \n",
    "        #输入x(32,1,28,28) 输出x(32,32,28,28)   输入尺寸不变是因为有padding,周围补0了\n",
    "        #卷积核尺寸 3*3*1  个数32\n",
    "        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1)\n",
    "        # weight 3*3*1*32=128  bias=32(因为此时有32[out_channels]个卷积核)\n",
    "        \n",
    "        #输入x(32,32,28,28) 输出x(32,32,28,28)  \n",
    "        #卷积核尺寸 3*3*32  个数32\n",
    "        self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=1)\n",
    "        # weight=3*3*32*32=9216  bias=32\n",
    "        # 池化 变为(32,32,14,14)\n",
    "        #输入x(32,32,14,14) 输出x(32,64,14,14)\n",
    "        #卷积核尺寸 3*3*64  个数64\n",
    "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n",
    "        # weight=3*3*64*32=18432  bias=64\n",
    "        \n",
    "        #输入x(32,64,14,14) 输出x(32,64,14,14)\n",
    "        #卷积核尺寸 3*3*64  个数64\n",
    "        self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)\n",
    "        #池化核大小为2（2*2），步长为2  (14-2)//2+1=7\n",
    "        # weight=3*3*64*64=36864  bias=64\n",
    "        # 池化 变为(32,64,7,7)\n",
    "        #输入x(32,64,7,7) 输出x(32,128,7,7)\n",
    "        #卷积核尺寸 3*3*128  个数128\n",
    "        self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)\n",
    "        # weight=3*3*128*64=73728  bias=128\n",
    "        \n",
    "        #输入x(32,128,7,7) 输出x(32,128,7,7)\n",
    "        #卷积核尺寸 3*3*128  个数128\n",
    "        self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)\n",
    "        #池化核大小为2（2*2），步长为2  (7-2)//2+1=3\n",
    "        #weight=3*3*128*128=147456  bias=128\n",
    "        # 池化 变为(32,128,3,3)\n",
    "        #通道数越来越多是因为我们要用更多的卷积核取提取更多的特征\n",
    "        \n",
    "        self.flatten = nn.Flatten()\n",
    "        #展平，将(32,128,3,3)展平成(32,1152)\n",
    "        # input shape is (28, 28, 1) so the fc1 layer in_features is 128 * 3 * 3\n",
    "        \n",
    "        #因为最终分类为10个类别，一定要是全连接，两层全连接是为了不衰减的太快\n",
    "        self.fc1 = nn.Linear(128 * 3 * 3, 128)\n",
    "        self.fc2 = nn.Linear(128, 10) #输出尺寸（32,10）\n",
    "\n",
    "        self.init_weights()\n",
    "\n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        act = self.activation\n",
    "        x = self.pool(act(self.conv2(act(self.conv1(x))))) # 1 * 28 * 28 -> 32 * 14 * 14\n",
    "        # print(x.shape)\n",
    "        x = self.pool(act(self.conv4(act(self.conv3(x))))) # 32 * 14 * 14 -> 64 * 7 * 7\n",
    "        # print(x.shape)\n",
    "        x = self.pool(act(self.conv6(act(self.conv5(x))))) # 64 * 7 * 7 -> 128 * 3 * 3\n",
    "        # print(x.shape)\n",
    "        x = self.flatten(x) # 128 * 3 * 3 ->1152\n",
    "        x = act(self.fc1(x)) # 1152 -> 128\n",
    "        x = self.fc2(x) # 128 -> 10\n",
    "        return x\n",
    "\n",
    "\n",
    "for idx, (key, value) in enumerate(CNN().named_parameters()):\n",
    "    print(f\"{key}\\tparamerters num: {np.prod(value.shape)}\") # 打印模型的参数信息\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conv1.weight\tparamerters num: 288\n",
      "conv1.bias\tparamerters num: 32\n",
      "conv2.weight\tparamerters num: 9216\n",
      "conv2.bias\tparamerters num: 32\n",
      "conv3.weight\tparamerters num: 18432\n",
      "conv3.bias\tparamerters num: 64\n",
      "conv4.weight\tparamerters num: 36864\n",
      "conv4.bias\tparamerters num: 64\n",
      "conv5.weight\tparamerters num: 73728\n",
      "conv5.bias\tparamerters num: 128\n",
      "conv6.weight\tparamerters num: 147456\n",
      "conv6.bias\tparamerters num: 128\n",
      "fc1.weight\tparamerters num: 147456\n",
      "fc1.bias\tparamerters num: 128\n",
      "fc2.weight\tparamerters num: 1280\n",
      "fc2.bias\tparamerters num: 10\n"
     ]
    }
   ],
   "execution_count": 7
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "计算模型总参数量"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.083597Z",
     "start_time": "2025-01-20T12:11:40.066936Z"
    }
   },
   "cell_type": "code",
   "source": [
    "def count_parameters(model): #计算模型总参数量\n",
    "    return sum(p.numel() for p in model.parameters() if p.requires_grad)\n",
    "count_parameters(CNN())"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "435306"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 8
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.331078Z",
     "start_time": "2025-01-20T12:11:40.084606Z"
    }
   },
   "cell_type": "code",
   "source": [
    "activation = \"relu\"\n",
    "model = CNN(activation)\n",
    "# model.to(device)\n",
    "img = torch.randn(32, 1, 28, 28)\n",
    "model(img).shape"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([32, 10])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "source": [
    "#不同尺寸的卷积核，padding，stride的效果\n",
    "class CNN1(nn.Module):\n",
    "    def __init__(self, activation=\"relu\"):\n",
    "        super().__init__()\n",
    "        self.activation = F.relu if activation == \"relu\" else F.selu\n",
    "        #输入通道数，图片是灰度图，所以是1，图片是彩色图，就是3，输出通道数，就是卷积核的个数（32,1,28,28）\n",
    "        #padding='same',图像尺寸保持不变  padding='valid',不进行补0\n",
    "        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=5,padding=2,stride=1)\n",
    "        #输入x(32,32,28,28) 输出x(32,32,28,28)\n",
    "        self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=1)\n",
    "        self.pool = nn.MaxPool2d(2, 2) #池化核大小为2（2*2），步长为2\n",
    "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n",
    "        self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)\n",
    "        self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)\n",
    "        self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)\n",
    "        self.flatten = nn.Flatten()\n",
    "        # input shape is (28, 28, 1) so the fc1 layer in_features is 128 * 3 * 3\n",
    "        self.fc1 = nn.Linear(128 * 3 * 3, 128)\n",
    "        self.fc2 = nn.Linear(128, 10) #输出尺寸（32,10）\n",
    "\n",
    "        self.init_weights()\n",
    "\n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        act = self.activation\n",
    "        x=act(self.conv1(x)) # 1 * 28 * 28 -> 32 * 28 * 28\n",
    "        print(x.shape)\n",
    "        # x=act(self.conv2(x)) # 32 * 28 * 28 -> 32 * 28 * 28\n",
    "        # print(x.shape)\n",
    "        # x = self.pool(x) # 32 * 28 * 28 -> 32 * 14 * 14\n",
    "        # print(x.shape)\n",
    "        # x=act(self.conv3(x)) # 32 * 14 * 14 -> 64 * 14 * 14\n",
    "        # print(x.shape)\n",
    "        # x=act(self.conv4(x)) # 64 * 14 * 14 -> 64 * 14 * 14\n",
    "        # print(x.shape)\n",
    "        # x = self.pool(x) # 32 * 14 * 14 -> 64 * 7 * 7\n",
    "        # print(x.shape)\n",
    "        # x=act(self.conv5(x)) # 64 * 7 * 7 -> 128 * 7 * 7\n",
    "        # print(x.shape)\n",
    "        # x=act(self.conv6(x)) # 128 * 7 * 7 -> 128 * 7 * 7\n",
    "        # print(x.shape)\n",
    "        # x = self.pool(x) # 128 * 7 * 7 -> 128 * 3 * 3\n",
    "        # print(x.shape)\n",
    "        # x = self.flatten(x) # 128 * 3 * 3 ->1152\n",
    "        # x = act(self.fc1(x)) # 1152 -> 128\n",
    "        # x = self.fc2(x) # 128 -> 10\n",
    "        return x\n",
    "activation = \"relu\"\n",
    "model = CNN1(activation)\n",
    "# model.to(device)\n",
    "img = torch.randn(1, 1, 28, 28)\n",
    "model(img).shape"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.358520Z",
     "start_time": "2025-01-20T12:11:40.332099Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 32, 28, 28])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 32, 28, 28])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 10
  },
  {
   "cell_type": "code",
   "source": [
    "class CNN2(nn.Module):\n",
    "    def __init__(self, activation=\"relu\"):\n",
    "        super(CNN, self).__init__()\n",
    "        self.activation = F.relu if activation == \"relu\" else F.selu\n",
    "        #输入通道数，图片是灰度图，所以是1，图片是彩色图，就是3，输出通道数，就是卷积核的个数（32,1,28,28）\n",
    "        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1)\n",
    "        #输入x(32,32,28,28) 输出x(32,32,28,28)\n",
    "        self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=1)\n",
    "        self.pool = nn.MaxPool2d(2, 2) #池化核大小为2（2*2），步长为2\n",
    "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n",
    "        self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)\n",
    "        self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)\n",
    "        self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)\n",
    "        self.flatten = nn.Flatten()\n",
    "        # input shape is (28, 28, 1) so the fc1 layer in_features is 128 * 3 * 3\n",
    "        self.fc1 = nn.Linear(128 * 3 * 3, 128)\n",
    "        self.fc2 = nn.Linear(128, 10) #输出尺寸（32,10）\n",
    "\n",
    "        self.init_weights()\n",
    "\n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        act = self.activation\n",
    "        x=act(self.conv1(x)) # 1 * 28 * 28 -> 32 * 28 * 28\n",
    "        print(x.shape)\n",
    "        x=act(self.conv2(x)) # 32 * 28 * 28 -> 32 * 28 * 28\n",
    "        print(x.shape)\n",
    "        x = self.pool(x) # 32 * 28 * 28 -> 32 * 14 * 14\n",
    "        print(x.shape)\n",
    "        x=act(self.conv3(x)) # 32 * 14 * 14 -> 64 * 14 * 14\n",
    "        print(x.shape)\n",
    "        x=act(self.conv4(x)) # 64 * 14 * 14 -> 64 * 14 * 14\n",
    "        print(x.shape)\n",
    "        x = self.pool(x) # 32 * 14 * 14 -> 64 * 7 * 7\n",
    "        print(x.shape)\n",
    "        x=act(self.conv5(x)) # 64 * 7 * 7 -> 128 * 7 * 7\n",
    "        print(x.shape)\n",
    "        x=act(self.conv6(x)) # 128 * 7 * 7 -> 128 * 7 * 7\n",
    "        print(x.shape)\n",
    "        x = self.pool(x) # 128 * 7 * 7 -> 128 * 3 * 3\n",
    "        print(x.shape)\n",
    "        x = self.flatten(x) # 128 * 3 * 3 ->1152\n",
    "        x = act(self.fc1(x)) # 1152 -> 128\n",
    "        x = self.fc2(x) # 128 -> 10\n",
    "        return x\n",
    "\n",
    "for idx, (key, value) in enumerate(CNN().named_parameters()):\n",
    "    print(f\"{key}\\tparamerters num: {np.prod(value.shape)}\") # 打印模型的参数信息\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:40.383683Z",
     "start_time": "2025-01-20T12:11:40.361034Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conv1.weight\tparamerters num: 288\n",
      "conv1.bias\tparamerters num: 32\n",
      "conv2.weight\tparamerters num: 9216\n",
      "conv2.bias\tparamerters num: 32\n",
      "conv3.weight\tparamerters num: 18432\n",
      "conv3.bias\tparamerters num: 64\n",
      "conv4.weight\tparamerters num: 36864\n",
      "conv4.bias\tparamerters num: 64\n",
      "conv5.weight\tparamerters num: 73728\n",
      "conv5.bias\tparamerters num: 128\n",
      "conv6.weight\tparamerters num: 147456\n",
      "conv6.bias\tparamerters num: 128\n",
      "fc1.weight\tparamerters num: 147456\n",
      "fc1.bias\tparamerters num: 128\n",
      "fc2.weight\tparamerters num: 1280\n",
      "fc2.bias\tparamerters num: 10\n"
     ]
    }
   ],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "source": [
    "from torchviz import make_dot\n",
    "\n",
    "# Assuming your model is already defined and named 'model'\n",
    "# Construct a dummy input\n",
    "dummy_input = torch.randn(1, 1, 28, 28)  # Replace with your input shape\n",
    "\n",
    "# Forward pass to generate the computation graph\n",
    "output = model(dummy_input)\n",
    "\n",
    "# Visualize the model architecture\n",
    "dot = make_dot(output, params=dict(model.named_parameters()))\n",
    "dot.render(\"model_CNN\", format=\"png\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:42.312060Z",
     "start_time": "2025-01-20T12:11:40.385692Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 32, 28, 28])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'model_CNN.png'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:42.323395Z",
     "start_time": "2025-01-20T12:11:42.314067Z"
    }
   },
   "source": [
    "#计算参数量\n",
    "print(f'conv1 - {1*3*3*32}') # 32个卷积核，每个卷积核大小为1*3*3\n",
    "print(f'conv2 - {32*3*3*32}') # 32个卷积核，每个卷积核大小为32*3*3\n",
    "print(f'conv3 - {32*3*3*64}')\n",
    "print(f'conv4 - {64*3*3*64}')\n",
    "print(f'conv5 - {64*3*3*128}')\n",
    "print(f'conv6 - {128*3*3*128}')\n",
    "print(f'fc1 - {1152*128}')\n",
    "print(f'fc2 - {128*10}')\n",
    "\n",
    "#对上面求和，总参数数目为：\n",
    "1*3*3*32 +32+ 32*3*3*32 +32+ 32*3*3*64 +64+ 64*3*3*64+64 + 64*3*3*128 +128+ 128*3*3*128 +128+ 128*3*3*128+128 + 128*10+10"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conv1 - 288\n",
      "conv2 - 9216\n",
      "conv3 - 18432\n",
      "conv4 - 36864\n",
      "conv5 - 73728\n",
      "conv6 - 147456\n",
      "fc1 - 147456\n",
      "fc2 - 1280\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "435306"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 13
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:11:43.488609Z",
     "start_time": "2025-01-20T12:11:42.326966Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)              # 验证集预测\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item()) # 将验证集损失加入列表\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist()) # 将验证集预测结果加入列表\n",
    "        label_list.extend(labels.cpu().numpy().tolist())# 将验证集真实标签加入列表\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list) # 计算验证集准确率\n",
    "    return np.mean(loss_list), acc # 返回验证集损失均值和准确率\n"
   ],
   "outputs": [],
   "execution_count": 14
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:12:33.821055Z",
     "start_time": "2025-01-20T12:11:43.489619Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 15
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:12:33.828719Z",
     "start_time": "2025-01-20T12:12:33.821563Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 16
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:12:33.836446Z",
     "start_time": "2025-01-20T12:12:33.829655Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 17
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T12:12:34.157023Z",
     "start_time": "2025-01-20T12:12:33.838450Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "activation = \"relu\"\n",
    "model = CNN(activation)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/cnn-{activation}\")\n",
    "tensorboard_callback.draw_model(model, [1, 1, 28, 28])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/cnn-{activation}\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10)\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 18
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "ExecuteTime": {
     "end_time": "2025-01-20T12:59:12.857150Z",
     "start_time": "2025-01-20T12:12:34.158031Z"
    }
   },
   "source": [
    "model = model.to(device)\n",
    "record = training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=1000\n",
    "    )"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/34380 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "d2413fbb1b5c4fa0b26d34f4afbd20b5"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 19
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T13:02:05.707893Z",
     "start_time": "2025-01-20T13:02:05.502030Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=500)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 22
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T13:02:16.310327Z",
     "start_time": "2025-01-20T13:02:07.978879Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(f\"checkpoints/cnn-{activation}/best.ckpt\", weights_only=True,map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, test_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3234\n",
      "accuracy: 0.9013\n"
     ]
    }
   ],
   "execution_count": 23
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
